package com.onlineauction.entities;

import java.io.Serializable;
import java.util.Date;
import java.util.Random;

import net.sf.jsr107cache.Cache;

import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.onlineauction.exceptions.KeyNameEmptyException;
import com.onlineauction.exceptions.UsernameAlreadyExistsException;
import com.onlineauction.exceptions.WrongPasswordException;
import com.onlineauction.helpers.CacheHandler;
import com.onlineauction.helpers.Constants;
import com.onlineauction.helpers.Constants.Usertype;

public class User extends GenericEntity implements Serializable {

	private static final long serialVersionUID = 1799268656061140259L;
	private String username;
	private String password;
	private Usertype usertype = Usertype.Seller;

	public User(String username, String password, Usertype usertype, boolean toCreate)
			throws UsernameAlreadyExistsException, KeyNameEmptyException {
		if (username == null || username.length() == 0) {
			throw new KeyNameEmptyException();
		}

		this.username = username;
		this.password = password;
		this.usertype = usertype;
		if (toCreate) {
			if (isUsernameAvailable(this.username)) {
				try {
					this.createAndStoreUserEntity();
				} catch (Exception e) {
					throw new UsernameAlreadyExistsException();
				}
			} else {
				throw new UsernameAlreadyExistsException();
			}
		}
	}

	public User(String username, String password) throws WrongPasswordException {
		Entity entity = getEntity(Constants.USER, username);
		if (entity == null) {
			throw new WrongPasswordException();
		}
		if (!password.equals((String) entity.getProperty(Constants.PASSWORD))) {
			throw new WrongPasswordException();
		}

		this.username = username;
		this.password = (String) entity.getProperty(Constants.PASSWORD);
		this.usertype = Usertype.valueOf((String) entity.getProperty(Constants.USERTYPE));
	}

	public User(String username) {
		Entity entity = getEntity(Constants.USER, username);

		if (entity != null) {
			this.username = username;
			this.password = (String) entity.getProperty(Constants.PASSWORD);
			this.usertype = Usertype.valueOf((String) entity.getProperty(Constants.USERTYPE));
		}
	}

	private Entity createAndStoreUserEntity() throws Exception {
		markUsernameUsed(this.username);
		Entity userEntity = new Entity(Constants.USER, username);
		userEntity.setProperty(Constants.USERTYPE, usertype.toString());
		userEntity.setProperty(Constants.PASSWORD, password);

		storeEntity(userEntity);
		return userEntity;
	}

	public void unsubscribeUser() {
		Random random = new Random(new Date().getTime());
		Long randomPassword = random.nextLong();
		this.password = randomPassword.toString();
		try {
			this.createAndStoreUserEntity();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 
	 * @param username
	 * @return true if username is available (the username will be marked
	 *         unavailable subsequently for a certain period of time until being
	 *         made permanent) false if username is not available
	 */
	public static boolean isUsernameAvailable(String username) {
		if (username == null || username.length() == 0) {
			return false;
		}

		Key key = KeyFactory.createKey("user", username);
		String keyString = KeyFactory.keyToString(key);

		Cache usernameCache = CacheHandler.getUsernameCache();
		if (usernameCache != null) {
			if (usernameCache.containsKey(keyString)) {
				return false;
			}
		}

		Entity entity = getEntity(Constants.USER, username);
		if (entity == null) {
			return true;
		} else {
			if (usernameCache != null) {
				try {
					usernameCache.put(keyString, true);
				} catch (Exception e) {
					// do nothing
				}
			}
			return false;
		}
	}

	private static void markUsernameUsed(String username) {
		if (username == null || username.length() == 0) {
			return;
		}

		Key key = KeyFactory.createKey("user", username);
		String keyString = KeyFactory.keyToString(key);

		Cache usernameCache = CacheHandler.getUsernameCache();
		if (usernameCache != null) {
			if (!usernameCache.containsKey(keyString)) {
				try {
					usernameCache.put(keyString, true);
				} catch (Exception e) {
					// do nothing
				}
			}
		}
	}

	public String getUsername() {
		return username;
	}

	public String getPassword() {
		return password;
	}

	public Usertype getUsertype() {
		return usertype;
	}
}
